/**
 * ReadXMLOrder.java
 *
 * Enterprise Java
 *
 * Author:  Matthew Braddy
 * Created: Oct 28, 2010
 *
 */

package java113.project.xml;

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;
import java.util.*;
import java.io.*;
import java.util.logging.*;
import java113.project.base.Charges;
import java113.project.base.Order;
import java113.project.util.*;

/**
 * This class parses the xml input file.
 * It also creates Order objects using the data parsed from the xml
 *
 * @author Matthew Braddy
 */

public class ReadXMLOrder extends DefaultHandler {

    private CharArrayWriter m_oContents;

    private Properties      properties;

    private Map             orders;

    private Order           order;

    private Charges         charges;

    private int             counter;

    private Logger          logger;

    /**
     * Class constructor for this object
     * This method initializes the object
     * @param properties
     */
    public ReadXMLOrder(Properties properties) {

        this.setProperties(properties);

        logger = Logger.getLogger(properties.getProperty("logger"));

        this.setCounter(0);

        this.setOrders(new HashMap());

        this.setCharges();

    }

    /**
     *
     * returns the properties object
     *
     * @return
     */
    public Properties getProperties() {

        return this.properties;

    }

    /**
     *
     * sets the properties object
     *
     * @param properties
     */
    public void setProperties(Properties properties) {

        this.properties = properties;

    }

    /**
     *
     * sets the orders map
     *
     * @param orders
     */
    public void setOrders(Map orders) {

        this.orders = orders;

    }

    /**
     *
     * gets the orders map
     *
     * @return
     */
    public Map getOrders() {

        return this.orders;

    }

    /**
     * gets the counter
     *
     * @return
     */
    public int getCounter() {

        return this.counter;

    }

    /**
     *
     * sets the counter
     *
     * @param counter
     */
    public void setCounter(int counter) {

        this.counter = counter;

    }

    /**
     * This method increments the counter
     * 
     */
    private void incrementCounter() {

        this.counter++;

    }

    /**
     * @return the m_oContents
     */
    public CharArrayWriter getM_oContents() {
        return m_oContents;
    }

    /**
     * @param m_oContents the m_oContents to set
     */
    public void setM_oContents(CharArrayWriter m_oContents) {
        this.m_oContents = m_oContents;
    }

    /**
     * @return the order
     */
    public Order getOrder() {
        return order;
    }

    /**
     * @param order the order to set
     */
    public void setOrder(Order order) {
        this.order = order;
    }

    /**
     * @return the charges
     */
    public Charges getCharges() {
        return charges;
    }

    /**
     *
     * This method creates a new Charges object and reads
     * the charges.dat file and sets the values in the Charges object
     * 
     */
    public void setCharges() {
        
        this.charges = new Charges();

        int[]    itemRange  = new int[4];
        double[] itemCharge = new double[4];
        String[] sarray     = new String[2];
        int      chargesCounter    = 0;

        BufferedReader br = null;

        try {

            String inputFilePath = properties.getProperty("path.input") +
                                   properties.getProperty("file.charges");

            if ( ( inputFilePath == null ) ||
                    ( inputFilePath.length() == 0 ) ) {
                throw new DocVsSecurityException( "Path cannot be empty",
                                               DocVsSecurityException.INPUT_FAIL,
                                               "inputFilePath",
                                               inputFilePath );
            } else if ( inputFilePath.length() > 512 ) {
                throw new DocVsSecurityException(
                                            "Path cannot exceed 512 characters",
                                            DocVsSecurityException.INPUT_FAIL,
                                            "inputFilePath",
                                            inputFilePath );
            } else if ( inputFilePath.indexOf( ' ' ) >= 0 ) {
                throw new DocVsSecurityException( "Path cannot contain spaces",
                                               DocVsSecurityException.INPUT_FAIL,
                                               "inputFilePath",
                                               inputFilePath );
            }

            if (properties.getProperty("webApp").equalsIgnoreCase("false")) {

                br = new BufferedReader( new FileReader( inputFilePath ) );

            } else {

                InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(inputFilePath);

                br = new BufferedReader( new InputStreamReader( stream ) );

            }

            getLogger().log(Level.INFO, inputFilePath +
                    " has been opened for reading.");

            while ( br.ready() ) {
                String s = br.readLine();

                sarray              = s.split( " " );
                itemRange[chargesCounter]  = Integer.parseInt( sarray[0] );
                itemCharge[chargesCounter] = Double.parseDouble( sarray[1] );

                chargesCounter++;

                getLogger().log(Level.INFO, "A line has been read in from " +
                        inputFilePath +
                        ".");

            }

        } catch (FileNotFoundException fne) {

            getLogger().log(Level.SEVERE,
                    "Encountered an exception: " + fne.getMessage());

            fne.printStackTrace();

        } catch (IOException ioe) {

            getLogger().log(Level.SEVERE,
                    "Encountered an exception: " + ioe.getMessage());

            ioe.printStackTrace();

        } catch (Exception e) {

            getLogger().log(Level.SEVERE,
                    "Encountered an exception: " + e.getMessage());

            e.printStackTrace();

        } finally {

            try {

                br.close();

            } catch (Exception e) {

                getLogger().log(Level.SEVERE,
                    "Encountered an exception: " + e.getMessage());

                e.printStackTrace();

            }

        }

        charges.setItemCharge(itemCharge);
        charges.setItemRange(itemRange);

    }

    /**
     * @return the logger
     */
    public Logger getLogger() {
        return logger;
    }

    /**
     * @param logger the logger to set
     */
    public void setLogger(Logger logger) {
        this.logger = logger;
    }


    /**
     * Used to initialize the class and parse the XML
     *
     * @throws Exception
     */
    public void load() throws Exception
    {
    	InputStream oInputStream;

        
        this.setM_oContents(new CharArrayWriter());  
        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser sp = spf.newSAXParser();
        ParserAdapter pa = new ParserAdapter(sp.getParser());
        pa.setContentHandler(this);

        String inputFilePath = properties.getProperty("path.input") +
                               properties.getProperty("xml.file.input");

        if ( ( inputFilePath == null ) ||
                    ( inputFilePath.length() == 0 ) ) {
                throw new DocVsSecurityException( "Path cannot be empty",
                                               DocVsSecurityException.INPUT_FAIL,
                                               "inputFilePath",
                                               inputFilePath );
            } else if ( inputFilePath.length() > 512 ) {
                throw new DocVsSecurityException(
                                            "Path cannot exceed 512 characters",
                                            DocVsSecurityException.INPUT_FAIL,
                                            "inputFilePath",
                                            inputFilePath );
            } else if ( inputFilePath.indexOf( ' ' ) >= 0 ) {
                throw new DocVsSecurityException( "Path cannot contain spaces",
                                               DocVsSecurityException.INPUT_FAIL,
                                               "inputFilePath",
                                               inputFilePath );
            }

        if (properties.getProperty("webApp").equalsIgnoreCase("true")) {

            oInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(inputFilePath);

        } else {

            oInputStream = new FileInputStream(inputFilePath);

        }

        
        getLogger().log(Level.INFO,  (inputFilePath +
                " has been opened for reading."));

        pa.parse(new InputSource(oInputStream));  
        getLogger().log(Level.INFO,  (inputFilePath +
                " has been parsed."));

    }

    /**
     * This method is used to determine the start point of an element in an
     * XML stream as it is being parsed; method is called when the parser has identified the beginning of
     * an element; hence, it is called for each and every beginning element found in the XML
     *
     * @param namespaceURI
     * @param qName
     * @param localName
     * @param attr
     * @throws SAXException
     */
    @Override
    public void startElement(String namespaceURI, String localName, String qName, Attributes attr) throws SAXException
    {

        String strHold;
        this.getM_oContents().reset();  //used to capture the current textual information for an element

        //here, we are checking the name of the elements that we are expecting to encounter in the XML; if an
        //element is encountered, then we will handle it; in our case, we may want to create an object instance and store it
        //since we will be using it; only elements that are currently being acted on are of interest; if we encounter an element
        //that we did not account for, then the data is lost as we will not do anything with it
        if(localName.equalsIgnoreCase("ORDER"))
        {

            setOrder(new Order());

        } else if(localName.equalsIgnoreCase("NAME")) {

           strHold = attr.getValue("custid");

           if(strHold != null && strHold.trim ().length () > 0) {

               int i = Integer.parseInt(strHold.trim());

                getOrder().setCustomerNumber(i);

           }

        } else if(localName.equalsIgnoreCase("ITEM")) {

           strHold = attr.getValue("name");
            System.out.println(strHold);
           if(strHold != null && strHold.trim ().length () > 0) {

                getOrder().setItem(strHold);



           }

        }
    }

    /**
     * This method is used to determine the end point of an element in an
     * XML stream as it is being parsed; it is called for each and every element that is closed in the
     * XML; the element data was stored in the m_oContents instance variable; we will extract the
     * accumulated text and place it where it belongs
     * @param namespaceURI
     * @param qName
     * @param localName
     * @throws SAXException
     */
    @Override
    public void endElement(String namespaceURI, String localName, String qName) throws SAXException
    {
        String strHold;

        
        if(localName.equalsIgnoreCase("ORDER")) {
            try {
                assignCharges(this.getOrder());
            } catch (DocVsSecurityException ex) {
                Logger.getLogger(ReadXMLOrder.class.getName()).log(Level.SEVERE, null, ex);
            }

            incrementCounter();

            this.orders.put(this.counter, this.getOrder());

            this.setOrder(null);

        } else if(localName.equalsIgnoreCase("NAME")) {


            strHold = this.getM_oContents().toString();

            getOrder().setCustomerName(strHold);

        } else if(localName.equalsIgnoreCase("QUANTITY")) {


            strHold = this.getM_oContents().toString();

            int i = Integer.parseInt(strHold.trim());

            getOrder().setQuantity(i);

        } else if(localName.equalsIgnoreCase("PRICE")) {


            strHold = this.getM_oContents().toString();

            double d = Double.valueOf(strHold.trim()).doubleValue();

            getOrder().setUnitPrice(d);

        }

    }

    /**
     * Override methods of the DefaultHandler class to gain notification of SAX Events. See
     * org.xml.sax.ContentHandler for all available events
     *
     * This method is used to retrieve the characters in the XML stream
     * @param ch
     * @param start
     * @param length
     * @throws SAXException
     */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException
    {
        this.getM_oContents().write(ch, start, length);
    }

    /**
     * Assigns the values from the Charges object to the Order object
     *
     * @param d
     * @throws DocVsSecurityException
     */
    public void assignCharges( Order d ) throws DocVsSecurityException {
        

        d.setHChargeQuantity( charges.getItemRange() );
        d.setHChargeAmount( charges.getItemCharge() );
    }

    /**
     * returns a string value of the instance variables
     *
     * @return
     */
    @Override
    public String toString() {

        return "char contents: " + m_oContents.toString() + "\n" +
                "properties: " + properties.toString() + "\n" +
                "orders: " + orders.toString() + "\n" +
                "order: " + order.toString() + "\n" +
                "counter: " + counter + "\n";

    }

}